import { BrowserWindow, clipboard, screen } from 'electron'
import { User32, ffi } from 'win32-api'
// @ts-ignore
import * as D from 'win32-def/def'

let isSetElectronForeground = false
let lastTargetRect: {
  left: number
  top: number
  right: number
  bottom: number
  width: number
  height: number
  isMinimized: boolean
} | null = null

const offsetMaps = {
  qn: 50,
  pdd: 40,
  dd: 40,
  jd: 36
}
let count = 0

const user32Api = User32.load()
const lib = ffi.load('user32.dll')
const user32 = {
  ...user32Api,
  SetActiveWindow: lib.func('SetActiveWindow', ffi.types.uint32, [ffi.types.uint32]),
  SetFocus: lib.func('SetFocus', ffi.types.uint32, [ffi.types.uint32]),
  keybd_event: lib.func('keybd_event', 'void', [
    ffi.types.uint8,
    ffi.types.uint8,
    ffi.types.uint32,
    ffi.types.uint32
  ])
}

const FindWindowFast = async (
  pid: number,
  className: string,
  titlePart: string
): Promise<number | null> => {
  // return 74126254
  let hwnd = user32.FindWindowExW(0, 0, className, null) // 先通过类名查找
  let windowPid: Buffer | null = null
  let titleBuffer: Buffer | null = null

  try {
    while (hwnd !== 0) {
      windowPid = Buffer.alloc(4)
      user32.GetWindowThreadProcessId(hwnd, windowPid)

      if (windowPid.readUInt32LE() === pid) {
        titleBuffer = Buffer.alloc(256 * 2) // 为Unicode字符分配空间
        if (user32.GetWindowTextW(hwnd, titleBuffer, 256) > 0) {
          const title = titleBuffer.toString('ucs2').replace(/\0+$/, '')
          if (title.includes(titlePart)) {
            return hwnd
          }
        }
        // 及时释放titleBuffer内存
        titleBuffer = null
      }

      hwnd = user32.FindWindowExW(0, hwnd, className, null) // 查找下一个同类窗口
      // 及时释放windowPid内存
      windowPid = null
    }
  } finally {
    // 确保所有Buffer都被释放
    windowPid = null
    titleBuffer = null
  }

  return null
}

const FindWindowRectPosition = (hwnd: number) => {
  if (hwnd === 0) {
    return null
  }
  const rect = Buffer.alloc(4 * 4)
  user32.GetWindowRect(hwnd, rect)
  return {
    left: rect.readInt32LE(0),
    top: rect.readInt32LE(4),
    right: rect.readInt32LE(8),
    bottom: rect.readInt32LE(12),
    width: rect.readInt32LE(8) - rect.readInt32LE(0),
    height: rect.readInt32LE(12) - rect.readInt32LE(4),
    isMinimized: user32.IsIconic(hwnd)
  }
}

const IsWindowForeground = (hwnd: number) => {
  return user32.GetForegroundWindow() === hwnd
}

// 计算自身窗口坐标
const calculateWindowPosition = (targetRect: {
  left: number
  top: number
  right: number
  bottom: number
  width: number
  height: number
}) => {
  const { top, right, height } = targetRect
  const scaleFactor = 1 / screen.getPrimaryDisplay().scaleFactor // 计算缩放因子

  if (globalThis.isCollapsed) {
    return {
      x: Math.round(right * scaleFactor) + 5,
      y: Math.round(top * scaleFactor),
      width: 60,
      height: 60
    }
  }

  const scaledRight = Math.round(right * scaleFactor)
  const scaledTop = Math.round(top * scaleFactor)
  const scaledHeight = Math.round(height * scaleFactor)
  const scaledWidth = Math.max(Math.round(300 * scaleFactor), 300)

  if (
    scaledRight + scaledWidth >= screen.getPrimaryDisplay().bounds.width &&
    !globalThis.isCollapsed
  ) {
    const offset = offsetMaps[globalThis.platFormGlobal] || 0
    globalThis.sideWindow = true
    return {
      x: scaledRight - scaledWidth + 5,
      y: scaledTop + Math.round(offset * scaleFactor),
      width: scaledWidth,
      height: scaledHeight - Math.round(offset * scaleFactor)
    }
  } else {
    globalThis.sideWindow = false
    return {
      x: scaledRight + 2,
      y: scaledTop,
      width: scaledWidth,
      height: scaledHeight + 2
    }
  }
}

const adjustWindowToTarget = async (
  mainWin: BrowserWindow,
  targetPid: number,
  targetClassName: string,
  targetTitlePart: string
) => {
  // 查找目标窗口
  const hwnd = (await FindWindowFast(targetPid, targetClassName, targetTitlePart)) ?? 0
  if (hwnd === 0) {
    mainWin.hide()
    console.error('未找到目标窗口')
    count++
    return
  } else {
    globalThis.windowHWND = hwnd
  }

  // 获取目标窗口位置信息
  const targetRect = FindWindowRectPosition(hwnd)
  if (!targetRect) {
    console.error('无法获取目标窗口位置')
    return
  }

  // 设置Electron窗口位置和大小，在目标窗口右边且高度保持一致
  const { isMinimized } = targetRect
  if (isMinimized) {
    mainWin.hide()
    return
  } else {
    mainWin.show()
  }

  // 计算移动客户端位置大小
  const { x, y, height, width } = calculateWindowPosition(targetRect)

  const currentRect = targetRect
  const isForeground = IsWindowForeground(hwnd)

  // 如果窗口位置未变化,且不是最小化和最前窗口状态，则跳过
  if (
    !currentRect ||
    !lastTargetRect ||
    currentRect.left !== lastTargetRect.left ||
    currentRect.top !== lastTargetRect.top ||
    currentRect.width !== lastTargetRect.width ||
    currentRect.height !== lastTargetRect.height ||
    currentRect.isMinimized ||
    isForeground
  ) {
    lastTargetRect = currentRect

    mainWin.setBounds({
      x,
      y,
      width,
      height
    })
  }

  // 如果目标窗口在前台，将Electron窗口也带到前台
  if (isForeground && !isSetElectronForeground) {
    mainWin.setAlwaysOnTop(true)
    isSetElectronForeground = true
  } else {
    mainWin.setAlwaysOnTop(false)
    isSetElectronForeground = false
  }
}

export default class Win32Utils {
  private static pollingInterval = 100
  private static followWindow: BrowserWindow
  private static targetWinName: string
  private static pid: number

  public static followTimerStop: any

  static pollFollowWindow(win: BrowserWindow, targetWinName: string, pid: number) {
    this.followWindow = win
    this.targetWinName = targetWinName
    this.pid = pid
    // 先立即执行一次
    adjustWindowToTarget(win, pid || 0, D.WNDCLASS_NAME, targetWinName)
    this.followWindow.show()

    // 然后设置定时器
    this.followTimerStop = setInterval(() => {
      adjustWindowToTarget(this.followWindow, this.pid || 0, D.WNDCLASS_NAME, this.targetWinName)
    }, this.pollingInterval)
  }

  static triggerLastXChange() {
    lastTargetRect = null
  }

  static stopFollowWindow() {
    lastTargetRect = null
    Win32Utils.followTimerStop && Win32Utils.followTimerStop()
  }

  static getWindowLastSize = () => {
    return {
      lastWidth: 300,
      lastHeight: lastTargetRect?.height || 0
    }
  }

  static focusAndPaste = async (HWND: number, text: string) => {
    try {
      if (!HWND) return
      // if (!HWND) return;
      // user32.SetForegroundWindow(HWND) // 窗口置顶
      // user32.SetActiveWindow(HWND) // 窗口激活
      // user32.SetFocus(HWND) // 窗口聚焦
      // 添加延迟以确保窗口已获得焦点
      if (!text) return HWND
      // 模拟粘贴
      clipboard.writeText(text)
      return HWND
    } catch (error) {
      return 0
    }
  }
}
